iT邦幫忙

0

單元測試的學習筆記 Day 1

  • 分享至 

  • xImage
  •  

為什麼要寫單元測試?

  • 透過單元測試,驗證開發的結果最終與預期的一致
  • 確保每段程式僅包含要驗證的業務邏輯

有了單元測試,我們對工作的定義範圍就比較注重了,因為多做要多花時間嘛!於是我們就跟主管商量,這兩週就做這些事,如果高層問起進度,就可以以兩週為單位回報,主管發現這樣他也很好做事,而且這群人交出來的東西真的不太會錯,於是每次回報進度也就不用傷腦筋抓 Buffer 了。
敏捷聖徒 Day 1:我不是專家 (Kuma)

Unit Test 要不要包含 DB ?

你就是不寫測試才會沒時間 2-12 (Kuma)

取決 DB 的管理,若開發者都是共用同一個 DB ,此情況 DB 屬於「外部系統」(視同呼叫第三方的 Api),故不包含測試,如果測試可以使用可以使用 H2 設定,此情況 DB 屬於「內部系統」,則可以單元測試,總的來說,Unit Test 不管怎麼定義,至少「不要牽扯外部系統」

單元測試

你就是不寫測試才會沒時間 2-2 (Kuma)

單元測試的起手三件套

  • expect 準備資料
  • actual 執行程式
  • verify 驗證結果
public class Student {
    private String firstName;
    private String lastName;
    
    public Student(String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }
    
    public String getFullName() {
        return firstName + " " + lastName;
    }
}
class StudentTest {
    @Test
    void full_name () {
        // expect
        Student student = new Student("Michael","Jordan");
        // actual
        String actual = student.getFullName();
        // verify
        assertEquals("Michael Jordan", actual)
    }
}

控制依賴

你就是不寫測試才會沒時間 3-2 (Kuma)

public boolean checkTime(ApplicationForm applicationForm) {

    Scholarship scholarship = scholarshipRepository.find(applicationForm.getScholarshipId());

    LocalDate deadline = scholarship.getDeadline();

    LocalDate today = LocalDate.now();

    return today.isEqual(deadline)
            || today.isBefore(deadline);

}

出現以下三個依賴

  • Application
    可以控制,直接 constructor 或 setter,控制 getter 的行為

  • ScholarshipRepository
    無法控制,因為沒有真實 DB,可以使用 Mockito,Mock DB 物件,並指定方法的輸出內容

    ScholarshipRepository fakeRepository = Mockito.mock(ScholarshipRepository.class);
    Mockito.when(fackRepository.find(777L)).thenReturn(new Scholarship(LocalDate.MAX);
    
  • LocalDate.now()
    無法控制,因為是 static,會隨著測試的準備階段自動初始方法,無法提前 Mock 取代,不過後來版本的 Mockito 對 static 的支援,因此可以覆蓋,如程式碼舉例

    LocalDate expected = LocalDate.of(2029, 12, 31);
    Mockito.mockStatic(LocalDate.class).when(LocalDate::now).thenReturn(expected);
    

    治本的方法,就是要意識到目前方法的實作與 static method LocalDate.now() 產生耦合,導致單元測試不容易撰寫,應該重構方法,參考 Dependency Inversion 來解偶,重構如下

    public boolean checkTime(Application application, Clock clock) {
        LocalDate today = clock.now();
        // ...
    
    LocalDate expected = LocalDate.of(2029, 12, 31);
    Clock fakeClock = Mockito.mock(Clock.class);
    Mockito.when(fackClock.now()).thenReturn(expected);
    

    方法經過重構後,方法原本依賴系統的時間,改成輸入自方法,方法的核心邏輯內聚提高,不受原本 LocalDate.now() 的實作引響

Mock 與 Stub

你就是不寫測試才會沒時間 3-5 (Kuma)

Stub

「可控的替代物件,用來取代一個外部相依物件」
如果我們先跟假物件串通好與待會要回傳給待測物件值,最後再去檢查待測物件的狀態,那它就是Stub。

Mock

「一個假物件,用來驗證待測物件是否如預期般呼叫這個假物件」
如果我們直接讓帶待物件互動,最後再去檢查剛剛假物件與待測物件的互動情形,那它就是 Mock。


圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言